/*
 * Copyright (c) 2008, 2010
 *      Toni Spets <toni.spets@iki.fi>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "common.h"
#include "thipx32.h"

#if defined(__cplusplus) && defined(BUILD_DLL)
extern "C" {
#endif

#define LAN_ONLY 0

uint16_t my_sock = 0;
extern struct sockaddr_in my_server;
extern struct sockaddr_in my_addr;

uint32_t WINAPI _IPX_Initialise()
{
    #ifndef CNCNET_CLIENT
    config_init();
    #endif

    #ifdef USE_UPNP
    upnp_run(config_port);
    #endif

    return 1;
}

uint32_t WINAPI _IPX_Open_Socket95(uint32_t sock)
{
    net_init();
    my_sock = net_socket();

    net_reuse(my_sock);

    net_address_ex(&my_addr, INADDR_ANY, config_port);
#if LAN_ONLY
    net_address_ex(&my_server, INADDR_BROADCAST, config_port);
#else
    net_address(&my_server, config_server, config_server_port);
#endif
    net_broadcast(my_sock);
    bind(my_sock, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_in));
    return 0;
}

uint32_t WINAPI _IPX_Start_Listening95()
{
    return 1;
}

uint32_t WINAPI _IPX_Get_Outstanding_Buffer95(void *ptr)
{
    struct sockaddr_in my_from;
    struct timeval tv;
    ra_packet_t *packet = (ra_packet_t *)ptr;

    uint16_t ret;

    /* WHY WESTWOOD?!?! WHY DO YOU CALL IPX FUNCTIONS WHEN PLAYING IN INTERNET MODE?! */
    if(!my_sock)
        return 0;

    fd_set read_fds;
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    FD_ZERO(&read_fds);
    FD_SET(my_sock, &read_fds);

    select(my_sock+1, &read_fds, NULL, NULL, &tv);

    if(FD_ISSET(my_sock, &read_fds))
    {
        memset(packet, 0, sizeof(ra_packet_t) - sizeof(ra_buffer_t));

        ret = net_recv(my_sock, (char *)&packet->buffer, sizeof(packet->buffer), &my_from);
        if(ret > 0) {
            if(ret < sizeof(ra_client_t))
                return 0;
            ret -= sizeof(ra_client_t);
            packet->len = htons((sizeof(ra_packet_t) - sizeof(packet->buffer.data)) + ret);
            if(packet->buffer.from.ip == 0) {
                packet->buffer.from.ip = my_from.sin_addr.s_addr;
                packet->buffer.from.port = my_from.sin_port;
            }
            return 1;
        }
    }

    return 0;
}

uint32_t WINAPI _IPX_Broadcast_Packet95(void *bufptr, uint32_t len)
{
    ra_buffer_t         buffer;

    memset(&buffer.from, 0, sizeof(ra_client_t));
    memcpy(&buffer.data, bufptr, len);

    if(config_port_force)
    {
        buffer.from.port = htons(config_port);
    }

    buffer.direct = 0x00;

    net_send(my_sock, (char *)&buffer, sizeof(ra_client_t)+len+1, &my_server);
    return 0;
}

uint32_t WINAPI _IPX_Send_Packet95(void *cliptr, void *bufptr, uint32_t len, void *unk1, void *unk2)
{
    ra_buffer_t         buffer;
    ra_client_t         *client = (ra_client_t*)cliptr;

    struct sockaddr_in addr;

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = client->ip;
    addr.sin_port = client->port;

    memset(&buffer.from, 0, sizeof(ra_client_t));
    memcpy(&buffer.data, bufptr, len);

    buffer.direct = 0x01;

    net_send(my_sock, (char *)&buffer, sizeof(ra_client_t)+len+1, &addr);
    return 1;
}

uint32_t WINAPI _IPX_Get_Connection_Number95()
{
    return 0;
}

uint32_t WINAPI _IPX_Get_Local_Target95( uint32_t *p1, uint32_t *p2, uint32_t *p3, uint32_t *p4 )
{
    return 1;
}

uint32_t WINAPI _IPX_Close_Socket95(uint32_t sock)
{
    closesocket(my_sock);
    my_sock = 0;
    net_free();
    return 0;
}

uint32_t WINAPI _IPX_Shut_Down95()
{
    return 1;
}

#if defined(__cplusplus) && defined(BUILD_DLL)
} // extern C
#endif
